package library.http;

import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.webkit.URLUtil;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.InetSocketAddress;
import java.util.Iterator;
import java.util.Map;

import library.http.annotations.BaseUrl;
import library.http.annotations.Proxy;
import library.http.e.Mode;
import library.logger.ILogger;
import library.utils.Utils;
import okhttp3.HttpUrl;


public class HttpUtils {
    private static ILogger iLogger;


    public static void init(@NonNull final Context context) {
        HttpStatic.init(context);
    }

    protected static <T> void checkInterface(final Class<T> service) {
        if (!service.isInterface()) {
            throw new IllegalArgumentException("API declarations must be interfaces.");
        }
        // Prevent API interfaces from extending other interfaces. This not only avoids a bug in
        // Android (http://b.android.com/58753) but it forces composition of API declarations which is
        // the recommended pattern.
        if (service.getInterfaces().length > 0) {
            throw new IllegalArgumentException("API interfaces must not extend other interfaces.");
        }
    }

    public static String getFullGetUrl(final String url, Map<String, Object>... params) {
        if (url == null)
            return null;
        StringBuilder sb = new StringBuilder();
        if (params != null) {
            for (Map<String, Object> map : params) {
                if (map == null)
                    continue;
                Iterator<Map.Entry<String, Object>> paramsIterator = map.entrySet().iterator();
                while (paramsIterator.hasNext()) {
                    Map.Entry<String, Object> entry = paramsIterator.next();
                    String key = entry.getKey();
                    Object value = entry.getValue();
                    sb.append(key).append("=").append(value != null ? value : "").append("&");
                }
            }
        }

        String fullUrl = url;
        String queries = sb.toString();
        if (queries != null && queries.endsWith("&"))
            queries = queries.substring(0, queries.length() - 1);
        if (!TextUtils.isEmpty(queries.trim())) {
            fullUrl += (url.endsWith("?") ? "&" : "?") + queries;
        }
        return fullUrl;
    }

    protected static Class getInterfaceGenericClass(final Object obj, final int indexOfInterface, final int indexOfType) {
        return getInterfaceGenericClass(obj != null ? obj.getClass() : null, indexOfInterface, indexOfType);
    }

    protected static Class getInterfaceGenericClass(final Class clazz, final int indexOfInterface, final int indexOfType) {
        if (clazz != null) {
            try {
                Type[] types = clazz.getGenericInterfaces();
                if (types != null) {
                    Type genericType = types[indexOfInterface];
                    return getGenericClass(genericType, indexOfType);
                }
            } catch (Exception e) {
                HttpUtils.debug(e);
            }
        }
        return null;
    }

    protected static Type getInterfaceGenericType(final Object obj, final int indexOfInterface, final int indexOfType) {
        return getInterfaceGenericType(obj != null ? obj.getClass() : null, indexOfInterface, indexOfType);
    }

    protected static Type getInterfaceGenericType(final Class clazz, final int indexOfInterface, final int indexOfType) {
        if (clazz != null) {
            try {
                Type[] types = clazz.getGenericInterfaces();
                if (types != null) {
                    Type genericType = types[indexOfInterface];
                    return getGenericType(genericType, indexOfType);
                }
            } catch (Exception e) {
                HttpUtils.debug(e);
            }
        }
        return null;
    }


    protected static Type getClassGenericType(final Object obj, final int index) {
        return getClassGenericType(obj != null ? obj.getClass() : null, index);
    }


    protected static Type getClassGenericType(final Class clazz, final int index) {
        if (clazz != null) {
            Type genericSuperclass = clazz.getGenericSuperclass();
            return getGenericType(genericSuperclass, index);
        }
        return null;
    }

    protected static Class getClassGenericClass(final Object obj, final int index) {
        return getClassGenericClass(obj != null ? obj.getClass() : null, index);
    }

    protected static Class getClassGenericClass(final Class clazz, final int index) {
        if (clazz != null) {
            Type genericSuperclass = clazz.getGenericSuperclass();
            return getGenericClass(genericSuperclass, index);
        }
        return null;
    }

    protected static Class getGenericClass(Type type, int index) {
        if (type instanceof ParameterizedType) {
            try {
                Type[] types = ((ParameterizedType) type).getActualTypeArguments();
                Type genericType = types[index];
                if (genericType instanceof Class)
                    return (Class) types[index];
                else if (genericType instanceof ParameterizedType) {
                    ParameterizedType actualType = (ParameterizedType) genericType;
                    return (Class) actualType.getRawType();
                }
            } catch (Exception e) {
                HttpUtils.debug(e);
            }
        }
        return null;
    }


    protected static Type getGenericType(Type type, int index) {
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type[] types = parameterizedType.getActualTypeArguments();
            return types[index];
        }
        return null;
    }

    public static java.net.Proxy getProxy(String host, int port) {
        try {
            Uri uri = Uri.parse(host);
            String scheme = uri.getScheme();
            if ("http".equals(scheme) || "https".equals(scheme)) {
                Uri.Builder builder = uri.buildUpon();
                builder.scheme("http");
                uri = builder.build();
                host = uri.toString();
            }
            InetSocketAddress address = new InetSocketAddress(host, port);
            return new java.net.Proxy(java.net.Proxy.Type.HTTP, address);
        } catch (Exception e) {
            HttpUtils.debug(e);
        }
        return null;
    }

    public static boolean isProxyAvailable(java.net.Proxy proxy) {
        return proxy != null
                && proxy.address() instanceof InetSocketAddress
                && Utils.ping((InetSocketAddress) proxy.address(), 200);
    }

    public static java.net.Proxy getProxy(Class i) {
        HttpUtils.checkInterface(i);
        Proxy proxy = (Proxy) i.getAnnotation(Proxy.class);
        if (proxy != null && !TextUtils.isEmpty(proxy.host())) {
            if ((proxy.mode() == Mode.DEBUG && Utils.isDebuggable(HttpStatic.getContext()))
                    || proxy.mode() == Mode.RELEASE) {
                return getProxy(proxy.host(), proxy.port());
            }
        }
        return null;
    }

    public static String getBaseUrl(Class i) {
        HttpUtils.checkInterface(i);
        BaseUrl baseUrl = (BaseUrl) i.getAnnotation(BaseUrl.class);
        return baseUrl != null ? baseUrl.value() : null;
    }

    /**
     * ****************************    debug   ********************************************
     */

    protected static void debug(String fmt, Object... args) {
        debug(String.format(fmt, args));
    }

    protected static void debug(Object log) {
        if (iLogger != null)
            iLogger.onOutput(log);
    }

    public static void setLogger(ILogger iLogger) {
        HttpUtils.iLogger = iLogger;
    }

    /**
     * ****************************    Error   ********************************************
     */
    protected static void error(String fmt, Object... args) {
        if (fmt == null)
            return;
        throw new IllegalArgumentException(String.format(fmt, args));
    }

    protected static void error(Method method, Exception e) {
        throw new IllegalArgumentException("\n"
                + "Class  : " + method.getDeclaringClass().getSimpleName() + "\n"
                + "Method : " + method.getName() + "\n"
                + "Error  : " + "see cause", e);
    }

    protected static void error(Method method, String message, Object... args) {
        message = String.format(message, args);
        throw new IllegalArgumentException("\n"
                + "Class  : " + method.getDeclaringClass().getSimpleName() + "\n"
                + "Method : " + method.getName() + "\n"
                + "Error  : " + message);
    }

    public static boolean isUrl(String data) {
        try {
            final HttpUrl newUrl = HttpUrl.parse(data);
            return newUrl != null && URLUtil.isNetworkUrl(data);
        } catch (Exception e) {
        }
        return false;
    }

    public static String parseUrl(String baseUrl, String url) {
        if (!TextUtils.isEmpty(url) && !URLUtil.isNetworkUrl(url)
                && !TextUtils.isEmpty(baseUrl) && URLUtil.isNetworkUrl(baseUrl)) {
            if (!url.startsWith("/")) {
                if (baseUrl.endsWith("/")) {
                    url = baseUrl + url;
                } else {
                    url = baseUrl + "/" + url;
                }
            } else {
                HttpUrl httpUrl = HttpUrl.parse(baseUrl).resolve(url);
                httpUrl = httpUrl.resolve(url);
                url = httpUrl.toString();
            }
        }
        try {
            return HttpUrl.parse(url).toString();
        } catch (Exception e) {

        }
        return url;
    }


}
